package com.terrier.net
{
    import flash.net.Socket;
    import flash.utils.Endian;

    /**
     * 默认 Socket 消息包读写策略对象
     * 消息包结构: 2字节的消息包长度 + 2字节的消息包ID + 数据;
     * 消息包的长度 = 2字节 + 2字节 + 数据长度;
     * @author Jarvis.weng
     */
    public class WxSocketDefaultStrategy implements IWxSocketStrategy
    {
        private static var _defaultEndian:String = Endian.LITTLE_ENDIAN;

        /**
         * 默认的字节序
         * @return
         */
        public static function get defaultEndian():String
        {
            return _defaultEndian;
        }

        /**
         * @private
         */
        public static function set defaultEndian(value:String):void
        {
            _defaultEndian = value;
        }

        private var _readMore:Boolean = false;

        private var _dataLength:uint = 0;

        private var _socket:Socket = null;

        private var _endian:String = defaultEndian;

        /**
         * 构造函数
         */
        public function WxSocketDefaultStrategy()
        {
            // no action
        }

        /**
         * 指示数据的字节顺序；
         * 可能的值为来自 flash.utils.Endian 类的常量，Endian.BIG_ENDIAN 或 Endian.LITTLE_ENDIAN。
         * @return
         */
        public function get endian():String
        {
            return _endian;
        }

        /**
         * @private
         */
        public function set endian(value:String):void
        {
            _endian = value;
        }

        /**
         * 初始化 Socket
         * @param socket
         */
        public function initSocket(socket:Socket):void
        {
            socket.endian = endian;
        }

        /**
         * 将数据写入Socket对象
         * @param socket Socket对象
         * @param data 写入的数据
         * @return 如果需要立刻发送数据,返回true;否则,返回false
         * @throws Error 如果发送数据的字节序和Socket的字节序有冲突,抛出异常
         */
        public function writeData(socket:Socket, data:*):Boolean
        {
            var netPack:WxNetPacket = data as WxNetPacket;
            if (netPack.endian != socket.endian)
            {
                throw new Error("Endian error");
            }
            socket.writeShort(netPack.length + 4);
            socket.writeShort(netPack.id);
            socket.writeBytes(netPack);
            return true;
        }

        /**
         * 从Socket对象中读取数据
         * @param socket Socket对象
         * @return 读取的数据
         */
        public function readData(socket:Socket):*
        {
            _readMore = false;

            if (_dataLength == 0)
            {
                if (socket.bytesAvailable < 2)
                {
                    // 长度头不完整,结束本次读取,返回空数据
                    return null;
                }
                // 长度头完整,读取数据长度; 
                // 这里的长度头是一个无符号短整型(2个字节),数据包长度不包含长度头本身
                _dataLength = socket.readUnsignedShort() - 2;
            }

            if (socket.bytesAvailable < _dataLength)
            {
                // 可读数据不完整,结束本次读取,返回空数据
                return null;
            }
            else
            {
                var data:WxNetPacket = new WxNetPacket();
                data.endian = endian;
                if (_dataLength >= 2)
                {
                    // 读取数据包id
                    data.id = socket.readUnsignedShort();
                    _dataLength -= 2;
                    if (_dataLength > 0)
                    {
                        // 读取剩余数据
                        socket.readBytes(data, 0, _dataLength);
                    }
                }
                // 长度头归零
                _dataLength = 0;
                // 检测缓冲区,如果仍有可读数据,设置标记,指示WxSocket继续读取
                _readMore = (socket.bytesAvailable > 0);
                // 返回完整数据
                return data;
            }
        }

        /**
         * 是否需要进一步读取
         * @return
         */
        public function get readMore():Boolean
        {
            return _readMore;
        }
    }
}
